async function generateExcel() {
    const statusEl = document.getElementById('status');
    statusEl.textContent = '正在生成销售单...';

    try {
        const workbook = new ExcelJS.Workbook();
        const worksheet = workbook.addWorksheet('销售单');

        // =============== 1. 设置列宽 ===============
        worksheet.columns = [
            { width: 10 }, { width: 20 }, { width: 25 }, { width: 10 },
            { width: 10 }, { width: 15 }, { width: 15 }, { width: 20 }
        ];

        // =============== 2. 合并单元格 ===============
        // 标题合并
        worksheet.mergeCells('A1:H1');
        // 付款说明合并
        worksheet.mergeCells('A27:H27');
        // 合并客户名称
        worksheet.mergeCells('B3:C3');
        // 合并工程地址
        worksheet.mergeCells('E3:F3');
        // 增加美观程度 合并第 2 行
        worksheet.mergeCells('A2:H2');
        // 增加美观程度 合并第 4 行
        worksheet.mergeCells('A4:H4');
        // 增加美观程度 合并第 24 行
        worksheet.mergeCells('A24:H24');
        // 增加美观程度 合并第 26 行
        worksheet.mergeCells('A26:H26');
        // 增加美观程度 合并 C22:F22
        worksheet.mergeCells('C22:F22');
        // 增加美观程度 合并 B23:D23
        worksheet.mergeCells('B23:D23');
        // 增加美观程度 合并 F23:H23
        worksheet.mergeCells('F23:H23');

        // =============== 3. 设置边框样式 ===============
        const borderStyle = {
            top: { style: 'thin' },
            left: { style: 'thin' },
            bottom: { style: 'thin' },
            right: { style: 'thin' }
        };

        // =============== 4. 添加标题 ===============
        const titleCell = worksheet.getCell('A1');
        titleCell.value = '威朗森系统门窗销售单';
        titleCell.font = { bold: true, size: 18 };
        titleCell.alignment = { horizontal: 'center', vertical: 'middle' };
        titleCell.border = borderStyle; // 添加边框

        // =============== 5. 客户信息行 ===============
        worksheet.getCell('A3').value = '客户名称：';
        worksheet.getCell('D3').value = '工程地址：';
        worksheet.getCell('G3').value = '订单编号：';

        worksheet.getCell('B3').value = user.name;
        worksheet.getCell('E3').value = user.address;
        worksheet.getCell('H3').value = sale.id;

        // 为信息行添加边框
        ['A3', 'D3', 'G3'].forEach(address => {
            worksheet.getCell(address).border = borderStyle;
        });

        // =============== 6. 表头行 ===============
        const headerRow = worksheet.getRow(5);
        headerRow.values = ['序号', '品名', '厚度', '数量', '平方', '单价', '金额', '备注'];
        headerRow.font = { bold: true, color: { argb: 'FFFFFF' } };
        headerRow.eachCell(cell => {
            cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: '4472C4' } };
            cell.alignment = { horizontal: 'center' };
            cell.border = borderStyle; // 添加边框
        });

        // =============== 7. 数据行 ===============
        // todo: 循环的长度为可续需求的长度
        for (let i = 0; i < saleProductsList.length; i++) {
            let saleProducts = saleProductsList[i]

            const row = worksheet.getRow(i + 6);
            row.getCell(1).value = i + 1;
            row.getCell(1).alignment = { horizontal: 'center' };
            row.getCell(7).value = { formula: `D${i + 6}*F${i + 6}`, date1904: false };
            row.getCell(7).numFmt = '#,##0.00';

            // todo: 在这里添加用户需求的销售产品
            row.getCell(2).value = saleProducts.products.name;
            row.getCell(3).value = saleProducts.products.model.thickness;
            row.getCell(4).value = saleProducts.number;
            row.getCell(5).value = saleProducts.products.model.size;
            row.getCell(6).value = saleProducts.products.price;
            row.getCell(8).value = saleProducts.remark? saleProducts.remark : '';

            // 为整行添加边框
            row.eachCell(cell => {
                cell.border = borderStyle;
            });
        }

        // =============== 8. 合计行 ===============
        worksheet.getCell('A22').value = '合计金额';
        worksheet.getCell('B22').value = { formula: 'G22', date1904: false };
        worksheet.getCell('G22').value = { formula: 'SUM(G6:G21)', date1904: false };
        worksheet.getCell('G22').numFmt = '#,##0.00';

        const totalRow = worksheet.getRow(22);
        totalRow.font = { bold: true };
        totalRow.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'F2F2F2' } };
        totalRow.eachCell(cell => {
            cell.border = borderStyle; // 添加边框
        });

        // =============== 9. 底部信息 ===============
        worksheet.getCell('A23').value = '备注信息';
        worksheet.getCell('E23').value = '收货单位';
        worksheet.getCell('E25').value = '日期: ';
        worksheet.getCell('F25').value = new Date().toLocaleDateString();

        worksheet.getCell('B23').value = '备注信息';
        worksheet.getCell('F23').value = user.address;
        worksheet.getCell('F25').value = sale.createDate;

        // 为底部单元格添加边框
        ['A23', 'E23', 'E25'].forEach(address => {
            worksheet.getCell(address).border = borderStyle;
        });

        // =============== 10. 付款说明 ===============
        const noteCell = worksheet.getCell('A27');
        noteCell.value = '定金70%到位安排生产，安装完毕后支付尾款';
        noteCell.font = { italic: true };
        noteCell.alignment = { horizontal: 'center' };
        noteCell.border = borderStyle; // 添加边框

        // =============== 11. 保存文件 ===============
        const buffer = await workbook.xlsx.writeBuffer();
        saveAs(new Blob([buffer]), user.name + user.mobile + '.xlsx');

        statusEl.textContent = "销售单生成成功！";
        statusEl.style.color = "green";
    } catch (err) {
        statusEl.textContent = "生成失败: " + err.message;
        statusEl.style.color = "red";
    }
}